#include "Amplitude.hpp"
#include "ElementaryOperator.hpp"
#include "IdentityOperator.hpp"
#include "KetBra.hpp"

#include <iostream>
#include <complex>

#define EPSILON 1.0e-15

void testKetBraConstructor(int *numPassed, int *numTests);
void testIdConstructor(int *numPassed, int *numTests);
void testKetBraMultiply(int *numPassed, int *numTests);
void testKetBraTimesId(int *numPassed, int *numTests);
void testIdTimesKetBra(int *numPassed, int *numTests);
void testIdTimesId(int *numPassed, int *numTests);

void printStatus(int status, int *numPassed);

int main(int argn, char **argv) {

	int numPassed = 0;
	int numTests = 0;


	std::cout << "\n ====== unit tests for ElementaryOperator\n";

  testKetBraConstructor(&numPassed, &numTests);
  testIdConstructor(&numPassed, &numTests);
  testKetBraMultiply(&numPassed, &numTests);
  testKetBraTimesId(&numPassed, &numTests);
  testIdTimesKetBra(&numPassed, &numTests);
  testIdTimesId(&numPassed, &numTests);

	std::cout << " ====== passed " << numPassed << "/" << numTests <<
		"\n" << std::endl;

	return 0;
}

void testKetBraConstructor(int *numPassed, int *numTests){
  KetBra sigmaMinus(0, 1, 1.0);
  ++*numTests;
	std::cout <<  "  testKetBraConstructor .................. ";
  if (sigmaMinus.getRow() == 0 &&
      sigmaMinus.getCol() == 1 &&
      std::abs(sigmaMinus.getAmplitude() - Amplitude(1.0)) < EPSILON) {
    printStatus(1, numPassed);
  } else {
    printStatus(0, numPassed);
  }
}

void testIdConstructor(int *numPassed, int *numTests) {
  IdentityOperator id;
  ++*numTests;
	std::cout <<  "  testIdConstructor ...................... ";
  printStatus(1, numPassed);
}

void testKetBraMultiply(int *numPassed, int *numTests) {
	std::cout <<  "  testKetBraMultiply ..................... ";
  ++*numTests;
  Amplitude a1(1.0, 3.0);
  Amplitude a2(3.0, 2.0);
  KetBra sigmaMinus(0, 1, a1);
  KetBra sigmaPlus(1, 0, a2);
  KetBra& result = static_cast<KetBra&>(*sigmaPlus.matMul(sigmaMinus));
  if (result.getRow() == 1 &&
      result.getCol() == 1 &&
      std::abs(result.getAmplitude() - a1 * a2) < EPSILON) {
    printStatus(1, numPassed);
  } else {
    printStatus(0, numPassed);
  }
}

void testKetBraTimesId(int *numPassed, int *numTests) {
	std::cout <<  "  testKetBraTimesId ...................... ";
  ++*numTests;
  Amplitude a1(1.0, 3.0);
  KetBra sigmaMinus(0, 1, a1);
  IdentityOperator id;
  KetBra& result = static_cast<KetBra&>(*sigmaMinus.matMul(id));
  if (result.getRow() == sigmaMinus.getRow() &&
      result.getCol() == sigmaMinus.getCol() &&
      std::abs(result.getAmplitude() - sigmaMinus.getAmplitude()) <
      EPSILON) {
    printStatus(1, numPassed);
  } else {
    printStatus(0, numPassed);
  }
}

void testIdTimesKetBra(int *numPassed, int *numTests) {
	std::cout <<  "  testIdTimesKetBra ...................... ";
  ++*numTests;
  Amplitude a1(1.0, 3.0);
  KetBra sigmaMinus(0, 1, a1);
  IdentityOperator id;
  KetBra& result = static_cast<KetBra&>(*id.matMul(sigmaMinus));
  if (result.getRow() == sigmaMinus.getRow() &&
      result.getCol() == sigmaMinus.getCol() &&
      std::abs(result.getAmplitude() - sigmaMinus.getAmplitude()) <
      EPSILON) {
    printStatus(1, numPassed);
  } else {
    printStatus(0, numPassed);
  }
}

void testIdTimesId(int *numPassed, int *numTests) {
  IdentityOperator id;
  id.matMul(id);
  ++*numTests;
	std::cout <<  "  testIdTimesId .......................... ";
  printStatus(1, numPassed);
}

void printStatus(int status, int *numPassed) {
  if (status) {
    ++*numPassed;
    std::cout << "passed." << std::endl;
  } else {
    std::cout << "failed." << std::endl;
  }
}

